/*
 *	Copyright (c) 2009, libBasement
 *	All rights reserved.
 *	
 *	Redistribution and use in source and binary forms, with or without modification,
 *	are permitted provided that the following conditions are met:
 *	
 *	Redistributions of source code must retain the above copyright notice,
 *	this list of conditions and the following disclaimer.
 *	Redistributions in binary form must reproduce the above copyright notice,
 *	this list of conditions and the following disclaimer in the documentation
 *	and/or other materials provided with the distribution.
 *	Neither the name of the <ORGANIZATION> nor the names of its contributors may
 *	be used to endorse or promote products derived from this software without
 *	specific prior written permission.
 *	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *	AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 *	THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *	ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
 *	FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *	(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 *	ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *	(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef BArray_h_
#define BArray_h_ 1

#include <BObject.h>
#include <BLambda.h>
#include <BEnumerator.h>
#include <vector>

namespace Basement
{
	typedef std::vector<Object *> ObjectVector;
	
#define CONCAT_(a, b) a ## b
#define CONCAT(a, b) CONCAT_(a, b)
#define forall(type, item, array) Array::ObjectEnumerator CONCAT(__u_enum, __LINE__) = array->objectEnumerator(); for (type item = NULL; (item = (type)CONCAT(__u_enum, __LINE__).nextObject()); )
	
	class Array : public Object
	{
	protected:
		ObjectVector *_objects;
	public:
		__BObjectStandardFunctions(Array);
		
		virtual void dealloc();
		
		static Array *array() { return Array::alloc()->init()->autorelease< Array * >(); }
		static Array *withObjects(Object *object, ...) __attribute__((sentinel));
		static Array *withArray(Array *array) { return Array::alloc()->initWithArray(array); }
		
		virtual Array *init();
		virtual Array *initWithObjects(Object *object, ...) __attribute__((sentinel));
		virtual Array *initWithObjects(Object *object, va_list arg);
		virtual Array *initWithArray(Array *other);
		
		virtual Array *copy() { return Array::alloc()->initWithArray(this); }
		
		//Basic Interaction
		virtual Object *objectAtIndex(UInteger index) const { return _objects->at(index); }
		virtual Object *operator[](UInteger index) { return this->objectAtIndex(index); }
		virtual UInteger count() const { return _objects->size(); }
		
		virtual String *description();
		virtual operator String *() { return this->description(); }
		
		virtual Object *firstObject() const { return _objects->front(); }
		virtual Object *lastObject() const { return _objects->back(); }
		
		virtual UInteger indexOfObject(Object *object);
		virtual UInteger indexOfObjectIdenticalTo(Object *object);
		
		virtual bool containsObject(Object *object) { return (this->indexOfObject(object) != Range::NotFound); }
		
		virtual bool isEqualToArray(Array *other) const;
		virtual bool isEqualTo(Array *other) const { return isEqualToArray(other); }
		
		//Enumeration
		virtual void foreach(void(*callback)(Array *, Object *));
		virtual void foreach(void(*callback)(Array *, Object *, UInteger));
		virtual void foreach(void(*callback)(Array *, UInteger));
		
		class ObjectEnumerator : public Enumerator
		{
			Array *_array;
			UInteger _index;
		public:
			ObjectEnumerator() : _array(0), _index(0) {}
			ObjectEnumerator(Array *array) : _array(array), _index(0) { _array->retain(); }
			~ObjectEnumerator() { _array->release(); }
			
			virtual Array *allObjects() { return _array; }
			virtual Object *nextObject()
			{
				if(_index == _array->count())
				{
					return NULL;
				}
				return _array->objectAtIndex(_index++);
			}
		};
		ObjectEnumerator objectEnumerator() { return ObjectEnumerator(this); }
		
		//Deriving New Arrays
		virtual Array *arrayByAddingObject(Object *object);
		virtual Array *operator+(Object *object) { return this->arrayByAddingObject(object); }
		virtual Array *arrayByAddingObjects(Array *objects);
		virtual Array *operator+(Array *objects) { return this->arrayByAddingObjects(objects); }
		virtual Array *subarrayWithRange(Range range);
		virtual Array *filteredArrayWith(bool(*callback)(Array *, Object *, void *userinfo), void *userinfo);
		
		virtual Array *sortedArrayUsingFunction(SortFunction callback, void *context);
		virtual void sortUsingFunction(SortFunction callback, void *context);
		
		//Mutating
		virtual void addObject(Object *object);
		virtual void addObjectsFromArray(Array *array);
		virtual void operator<<(Object *object) { this->addObject(object); }
		virtual void operator+=(Object *object) { this->addObject(object); }
		virtual void insertObject(Object *object, UInteger index);
		
		virtual void removeObjectAtIndex(UInteger index);
		virtual void removeObjectsInRange(Range &range);
		virtual void removeObjects(Array *objects);
		virtual void removeObject(Object *object);
		virtual void removeAllObjects() { _objects->clear(); }
	};
	
	extern Array *$(Object *object, ...);
}

#endif /* BArray_h_ */
